package com.start.pay.core;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.start.common.exceptions.StartBusException;
import com.start.common.tools.SpringContextTools;
import com.start.common.tools.StartEnumTools;
import com.start.common.tools.UUIdTools;
import com.start.pay.core.entity.StartOrderPayStateQueryResult;
import com.start.pay.core.entity.StartOrderRefundCallLogEntity;
import com.start.pay.core.entity.StartPayOrderEntity;
import com.start.pay.core.enums.StartPayState;
import com.start.pay.core.event.GlobalStartPayOrderEventDispatcher;
import com.start.pay.core.interfaces.StartBasePayPlatform;
import com.start.pay.core.log.StartPayLogManage;
import com.start.pay.core.model.PlatFormCreatePayResult;
import com.start.pay.core.model.PlatformOrderPayResult;
import com.start.pay.core.model.PlatformOrderResult;
import com.start.pay.core.model.PlatformResult;
import com.start.pay.core.service.StartPayOrderService;
import com.start.security.StartTokenTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Objects;

/**
 * 支付服务
 */
@Slf4j
public class StartPayServerService {
    private static final StartPayServerService server = new StartPayServerService();
    private boolean isInit = false;
    private GlobalStartPayOrderEventDispatcher dispatcher;
    private StartGlobalPayConfig startGlobalPayConfig;

    public static StartPayServerService getInstance() {
        if (server.isInit)
            return server;
        synchronized (StartPayServerService.class) {
            if (server.isInit) {
                return server;
            }
            server.init();
            server.isInit = true;
            return server;
        }
    }

    private StartPayOrderService startPayOrderService;

    private void init() {
        startPayOrderService = SpringContextTools.getBean(StartPayOrderService.class);
        startGlobalPayConfig = SpringContextTools.getBean(StartGlobalPayConfig.class);
        dispatcher = GlobalStartPayOrderEventDispatcher.getInstance();
    }


    /**
     * 创建订单
     *
     * @param platformType
     * @param appId
     * @param entity
     * @return
     */
    @Transactional
    public String createOrder(String platformType, String appId, StartPayOrderEntity entity) {
        if (StringUtils.isAllBlank(platformType)) {
            throw new StartBusException("platformType不能为空");
        }
        if (StringUtils.isAllBlank(appId)) {
            throw new StartBusException("appId不能为空");
        }
        if (!StartPayPlatformManager.getInstance().hasPlatform(platformType, appId)) {
            throw new StartBusException(String.format("未找到有效支付平台配置：{%s：%s}", platformType, appId));
        }
        if (entity.getUserOpenId() == null || StringUtils.isEmpty(entity.getUserOpenId())) {
            throw new StartBusException("userOpenId不能为空");
        }
        if (entity.getReceivableFee() == null) {
            entity.setReceivableFee(entity.getTotalFee());
        }

        entity.setExpireTime(LocalDateTime.now().plus(startGlobalPayConfig.getExpireTime(), ChronoUnit.SECONDS));

        entity.setOrderNo(UUIdTools.createSnowFakeId());
        entity.setPlatformType(platformType);
        entity.setUserType(StartTokenTools.getCurrentUser().getStartBaseUser().getUserType());
        entity.setStartUserType(StartTokenTools.getCurrentUser().getStartUserType());
        entity.setUserId(StartTokenTools.getCurrentUser().getUserId());
        entity.setAppId(appId);
        entity.setPayState(StartPayState.UN_PAY.getValue());
        dispatcher.onCreateOrderBefore(entity);
        entity.setQuantity(entity.getQuantity() == null || entity.getQuantity() < 1 ? 1 : entity.getQuantity());
        entity.setPrice(entity.getPrice() == null || entity.getPrice() < 0 ? 0 : entity.getPrice());
        if (entity.getTotalFee() == null) {// 当实收金额为空时计算实收金额
            entity.setTotalFee(entity.getPrice() * entity.getQuantity());
        }
        //计算优惠后价格
        if (entity.getCouponFee() != null && entity.getCouponFee() > 0) {
            entity.setTotalFee(entity.getReceivableFee() - entity.getCouponFee());
        }
        startPayOrderService.save(entity);
        dispatcher.onCreateOrderAfter(entity);
        return entity.getOrderNo();
    }

    /**
     * 创建支付信息
     *
     * @param orderNo
     * @return
     */
    public PlatFormCreatePayResult createPay(String orderNo) {
        StartPayOrderEntity orderEntity = startPayOrderService.getStartPayOrderByOrderNo(orderNo);
        if (orderEntity == null) throw new StartBusException("订单不存在");
        if (orderEntity.getPayState() != 0) throw new StartBusException("订单状态异常暂时无法进行支付");
        if (orderEntity.getExpireTime() != null) {
            if (!startGlobalPayConfig.hasValid(LocalDateTime.now(), orderEntity.getExpireTime())) {
                throw new StartBusException("订单交易已关闭无法支付");
            }
        }
        dispatcher.onPayOrderBefore(orderEntity);
        PlatFormCreatePayResult payResult = new PlatFormCreatePayResult();

        if (orderEntity.getTotalFee() < 1) {//应收金额小于1时直接支付成功
            orderEntity.setPayState(StartPayState.PAY_SUCCESS.getValue());
            startPayOrderService.updateById(orderEntity);
            dispatcher.onPayOrderSuccess(orderEntity);
            payResult.setPayState(orderEntity.getPayState());
            return payResult;
        }
        PlatformResult result = getPayPlatform(orderEntity.getPlatformType()).createPay(orderEntity);
        if (!result.isSuccess()) {
            log.error(result.getErrorReason());
            throw new StartBusException("创建支付失败请稍后再试");
        }
        if (result.getPayState() == null) {
            throw new StartBusException("支付状态不能为空");
        }
        payResult.setPlatformResult(result.getResultData());
        if (result.getPayState() == StartPayState.PAY_SUCCESS) {//第三方平台支付状态为支付成功时直接支付成功
            orderEntity.setPayState(StartPayState.PAY_SUCCESS.getValue());
            startPayOrderService.updateById(orderEntity);
        } else if (result.getPayState() == StartPayState.PAY_ING) {//第三方平台支付状态支付中时设置订单为支付中
            orderEntity.setPayState(StartPayState.PAY_ING.getValue());
            startPayOrderService.updateById(orderEntity);
            StartOrderCheckServer.getInstance().addOrder(orderNo);
        }
        payResult.setPayState(orderEntity.getPayState());
        return payResult;
    }

    /**
     * 是否存在订单
     *
     * @param orderNo
     * @return
     */

    public Boolean hasOrder(String orderNo) {
        LambdaQueryWrapper<StartPayOrderEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StartPayOrderEntity::getOrderNo, orderNo);
        return startPayOrderService.count(queryWrapper) > 0;
    }

    /**
     * 取消订单
     *
     * @param orderNo
     * @return
     */

    public boolean cancelOrder(String orderNo) {
        StartPayOrderEntity orderEntity = startPayOrderService.getStartPayOrderByOrderNo(orderNo);
        if (orderEntity == null) throw new StartBusException("订单不存在");
        if (!Objects.equals(orderEntity.getPayState(), StartPayState.UN_PAY.getValue())) {
            log.warn("orderNo：" + orderNo + " payState:" + orderEntity.getPayState());
            throw new StartBusException("订单状态不正确暂时无法取消订单");
        }

        dispatcher.onCancelOrderBefore(orderEntity);
        orderEntity.setPayState(StartPayState.PAY_CANCEL.getValue());
        startPayOrderService.updateById(orderEntity);
        dispatcher.onCancelOrderAfter(orderEntity);
        return true;
    }


    /**
     * 查询订单支付状态
     *
     * @param orderNo
     * @return
     */
    public StartOrderPayStateQueryResult queryOrderPayState(String orderNo) {
        StartPayOrderEntity entity = startPayOrderService.getStartPayOrderByOrderNo(orderNo);
        if (entity == null) {
            throw new StartBusException("无效订单");
        }
        StartPayState payState = StartEnumTools.findEnumByValue(StartPayState.class, entity.getPayState());
        if (payState == null) {
            throw new StartBusException("未知订单支付状态");
        }

        StartOrderPayStateQueryResult result = new StartOrderPayStateQueryResult();
        result.setStartOrderPayState(entity.getPayState());

        switch (payState) {
            case PAY_ING:
            case PAY_REFUND_ING:
            case UN_PAY:
                StartBasePayPlatform platform = StartPayPlatformManager.getInstance().getPlatform(entity.getPlatformType());
                PlatformOrderPayResult platformOrderPayResult = platform.queryPayOrder(entity);
                if (!platformOrderPayResult.isQueryState()) {
                    throw new StartBusException("订单查询错误");
                }
                StartPayState platformPayState = StartEnumTools.findEnumByValue(StartPayState.class, platformOrderPayResult.getPayState());
                if (platformPayState == null) {
                    throw new StartBusException("未知订单状态");
                }
                if (!platformOrderPayResult.getPayState().equals(entity.getPayState())) {
                    entity.setPayState(platformPayState.getValue());
                }
                startPayOrderService.updateById(entity);

                if (platformPayState == StartPayState.PAY_SUCCESS) {
                    dispatcher.onPayOrderSuccess(entity);
                } else if (platformPayState == StartPayState.PAY_REFUND_SUCCESS) {
                    dispatcher.onOrderRefundSuccess(entity);
                }
                result.setPlatformPayState(platformOrderPayResult.getPayState());
                break;
            default:
                result.setPlatformPayState(result.getStartOrderPayState());
                break;
        }

        return result;
    }

    /**
     * 根据订单号查询订单
     *
     * @param orderNo
     * @return
     */
    public StartPayOrderEntity queryOrder(String orderNo) {
        StartPayOrderEntity entity = startPayOrderService.getStartPayOrderByOrderNo(orderNo);
        if (entity == null) {
            throw new StartBusException("无效订单");
        }
        return entity;
    }

    /**
     * 支付回调处理
     *
     * @param platformType
     * @param appId
     * @param platformCallData
     * @return
     */

    public Object payCallBack(String platformType, String appId, String platformCallData) {

        PlatformOrderResult platformResult = getPayPlatform(platformType).payCallBack(appId, platformCallData);
        /**
         * 写入日志
         */
        StartOrderRefundCallLogEntity log = StartOrderRefundCallLogEntity.createLog(platformType, appId, platformCallData);
        log.setHandlerExMsg(platformResult.getErrorReason());
        log.setOrderNo(platformResult.getOrderNo());
        StartPayLogManage.log(log);

        /**
         * 处理具体业务逻辑
         */

        if (!platformResult.isSuccess()) {
            if (StringUtils.isNotEmpty(platformResult.getOrderNo())) {
                StartPayOrderEntity orderEntity = startPayOrderService.getStartPayOrderByOrderNo(platformResult.getOrderNo());
                if (orderEntity != null) {
                    orderEntity.setPayState(StartPayState.PAY_EXCEPTION.getValue());
                    startPayOrderService.updateById(orderEntity);
                    dispatcher.onPayOrderError(orderEntity, platformResult.getErrorReason());
                }
            }
            return platformResult.getResultData();
        }
        if (StringUtils.isEmpty(platformResult.getOrderNo())) {
            return platformResult.getResultData();
        }
        LambdaQueryWrapper<StartPayOrderEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StartPayOrderEntity::getOrderNo, platformResult.getOrderNo());
        StartPayOrderEntity orderEntity = startPayOrderService.getOne(queryWrapper);
        if (orderEntity == null) return platformResult.getResultData();

        orderEntity.setPayState(StartPayState.PAY_SUCCESS.getValue());
        startPayOrderService.updateById(orderEntity);
        dispatcher.onPayOrderSuccess(orderEntity);
        dispatcher.onPayOrderAfter(orderEntity);
        return platformResult.getResultData();
    }


    /**
     * 退款订单
     *
     * @param orderNo
     * @return
     */
    public Object refundOrder(String orderNo) {
        StartPayOrderEntity orderEntity = startPayOrderService.getStartPayOrderByOrderNo(orderNo);
        if (orderEntity == null) throw new StartBusException("订单不存在");
        if (orderEntity.getPayState() != 2) {
            throw new StartBusException("该订单未支付");
        }
        dispatcher.onOrderRefundBefore(orderEntity);
        PlatformResult platformResult = getPayPlatform(orderEntity.getPlatformType()).refundOrder(orderEntity);
        if (!platformResult.isSuccess()) {
            orderEntity.setPayState(StartPayState.PAY_REFUND_EXCEPTION.getValue());
        } else {
            orderEntity.setPayState(StartPayState.PAY_REFUND_ING.getValue());
            StartOrderCheckServer.getInstance().addOrder(orderNo);
        }
        startPayOrderService.updateById(orderEntity);
        return platformResult.getResultData();
    }

    /**
     * 退款回调
     *
     * @param platformType
     * @param appId
     * @param platformCallData
     * @return
     */

    public Object refundCallBack(String platformType, String appId, String platformCallData) {
        PlatformOrderResult platformOrderResult = getPayPlatform(platformType).refundCallBack(appId, platformCallData);

        /**
         * 写入日志
         */
        StartOrderRefundCallLogEntity log = StartOrderRefundCallLogEntity.createLog(platformType, appId, platformCallData);
        log.setHandlerExMsg(platformOrderResult.getErrorReason());
        log.setOrderNo(platformOrderResult.getOrderNo());
        StartPayLogManage.log(log);
        /**
         * 处理业务
         */
        if (!platformOrderResult.isSuccess()) {
            if (StringUtils.isNotEmpty(platformOrderResult.getOrderNo())) {
                StartPayOrderEntity orderEntity = startPayOrderService.getStartPayOrderByOrderNo(platformOrderResult.getOrderNo());
                if (orderEntity != null) {
                    orderEntity.setPayState(StartPayState.PAY_REFUND_EXCEPTION.getValue());
                    startPayOrderService.updateById(orderEntity);
                    dispatcher.onOrderRefundError(orderEntity, platformOrderResult.getErrorReason());
                }
            }
            return platformOrderResult.getResultData();
        }

        StartPayOrderEntity orderEntity = startPayOrderService.getStartPayOrderByOrderNo(platformOrderResult.getOrderNo());
        if (orderEntity == null) {
            return platformOrderResult.getResultData();
        }
        orderEntity.setPayState(StartPayState.PAY_REFUND_SUCCESS.getValue());
        startPayOrderService.updateById(orderEntity);
        dispatcher.onOrderRefundSuccess(orderEntity);
        dispatcher.onOrderRefundAfter(orderEntity);
        return platformOrderResult.getResultData();
    }

    public PlatformOrderPayResult queryPayPlatform(StartPayOrderEntity orderEntity) {
        StartBasePayPlatform payPlatform = getPayPlatform(orderEntity.getPlatformType());
        return payPlatform.queryPayOrder(orderEntity);
    }

    public PlatformOrderPayResult queryRefundOrderPlatform(StartPayOrderEntity orderEntity) {
        StartBasePayPlatform payPlatform = getPayPlatform(orderEntity.getPlatformType());
        return payPlatform.queryRefundOrder(orderEntity);
    }


    private StartBasePayPlatform getPayPlatform(String platformType) {
        return StartPayPlatformManager.getInstance().getPlatform(platformType);
    }


}
